/**
 * \file kernel/main.c
 * \author Davide Gessa
 * \date 10-12-09
 */

/*! \mainpage SpiderPig Documentation
 *
 * \section intro_sec Introduction
 *  SpiderPigOS is an unix-like operating system programmed in C, ASM and
 *  Python, that include a kernel, an installation suite and some programs.
 *  The system is full opensource and the target is to realize a tiny system
 *  that support many platform and many hardware in general; x86 32bit arch 
 *  is implemented for first.
 *   
 * \section feat_sec Features
 *  Here is a list of principal spiderpig features:
 *   - Multiplatform: x86
 *   - Monolitic kernel
 *   - Unix-Like system
 *   - Multitasking with preemptive switching
 * 
 * \section install_sec Installation
 *  There are two way to install spiderpig: use a prebuild live iso cd
 *  with installer and install it by this, or compile a custom system
 *  for your platform and install it by flashing the system into a storage. 
 *
 * 	\subsection step1 Step 1: Compiling kernel
 *   The first thing to do is create a custom kernel configuration. Start
 *   configurator kselect with the command "python kselect.py" in the
 *   directory of the kernel. You can select a presetted configuration,
 *   or select options, arch and drivers manually. At the end, when program
 *   ask you if you want to compile the kernel, click yes. If there's no
 *   errors, kselect should create a file called "kernel" that is indeed
 *   the compiled kernel.
 * 
 *  \subsection step2 Step 2: Select and compile programs and libs
 * 
 * 
 *  Now, you had to choose a way: "Flash Install" or "ISO Install".
 * 
 *  \subsection iso_inst ISO Install 
 *   \subsection step3a Step 3a: Create the iso
 *    Return in main spiderpig folder (where there are src, doc, etc) and
 *    type "./create_iso.sh". It create an image called "image.iso".
 * 
 *   \subsection step4a Step 4a: Burning the iso
 *    Now that the iso has been correctly created, you had to burn it in 
 *    a cd; if you haven't any cdrom drive in the pc where you want to install
 *    SpiderPigOS, you can flash it into a usb drive with the program
 *    unetbootin (http://unetbootin.sourceforge.net/)
 * 
 *   \subsection step5a Step 5a: Booting and installation program
 *    Insert cd or usb drive on cd or usb and reboot the pc; when pc prompt
 *    a grub window, select spiderpig install.
 * 
 *  \subsection flash_inst Flash Install
 *   \subsection step3b Step 3b: Create raw image
 *    Return in main spiderpig folder (where there are src, doc, etc) and
 *    type "./create_raw.sh". It create a raw image disk called "image.raw".
 *    
 *   \subsection step4b Step 4b: Flash in the media
 *    There are many device where you can flash the image.raw, and each
 *    device have a custom installation method: for example, if you want
 *    to flash the image in a usb disk connected on a pc, you can use dd
 *    or rawwrite with a command like this "dd if=image.raw of=/dev/sdb1"
 * 
 *   \subsection step4b Step 5b: Bootloader installation
 *    Each platform have a custom method to boot a system. image.raw have
 *    inside a kernel loader that can be chainloaded by a bootloader like
 *    grub or lilo, or directly loaded by the hardware.
 * 
 * \section use_sec Use it
 *  Now that you have installed SpiderPigOS in your hardware, you can use
 *  it.
 */


#include <main.h>
#include <video.h>
#include <arch.h>
#include <types.h>
#include <syscall.h>
#include <task/task.h>
#include <fs/fs.h>
#include <network/network.h>
#include <mm/mm.h>
#include <debug.h>
#include <util.h>
#include <driver.h>
#include <bish.h>
#include <config.h>
#include <driver/video/vesa.h>
#include <screenpig/main.h>


/**
 * Carica il sistema
 *
 * \param mb puntatore al multiboot header
 */
int main(multiboot_info_t *mb)
{
	#ifdef DEBUG
		debug_init();
		#ifdef DEBUG_REG_TRAC
			debug_reg_trace(1);
		#endif
	#endif
	
	
	DEBUGP("main()\n");

	mod_addr = (void *) ((multiboot_module_t *) mb->mods_addr)->mod_start;


	video_set_color(VIDEO_HI_WHITE_BLACK);
	video_clear();
	
	video_puts_colored(VIDEO_HI_BLUE_BLACK, "-> ");
	video_puts_colored(VIDEO_HI_BLUE_BLACK, MSG_WELCOME);
	video_puts_colored(VIDEO_HI_BLUE_BLACK, NAME);
	video_puts_colored(VIDEO_HI_BLUE_BLACK, " ");
	video_puts_colored(VIDEO_HI_BLUE_BLACK, VER);
	video_puts_colored(VIDEO_HI_BLUE_BLACK, " ");
	video_puts_colored(VIDEO_HI_BLUE_BLACK, REV);
	video_puts("\n");
	
	
	// Arch based
	video_puts_colored(VIDEO_HI_BLUE_BLACK, " -> ");
	video_puts(MSG_ARCH_INIT);
	arch_init();
	printf("%s (%s)\n", ARCH, CPU_VENDOR);
	
	// Memory manager
	video_puts_colored(VIDEO_HI_BLUE_BLACK, " -> ");
	video_puts(MSG_MM_INIT); 
	mm_init(mb->mem_lower, mb->mem_upper);
	video_puts("(");
	video_putint((mm_get_total_mem() / 1024));
	video_puts("mb)\n");
	
	// Driver manager
	video_puts_colored(VIDEO_HI_BLUE_BLACK, " -> ");
	video_puts(MSG_DRV_INIT); 
	driver_init();
		
	// Syscalls
	video_puts_colored(VIDEO_HI_BLUE_BLACK, " -> ");
	video_puts(MSG_SYSCALL_INIT);
	syscall_init();

	// Task Manager
	video_puts_colored(VIDEO_HI_BLUE_BLACK, " -> ");
	video_puts(MSG_TASK_INIT);	
	task_init(TASK_SWITCH_ROUND_ROBIN);	
	
	
	// Controlliamo prima se e' possibile avviare la gui
	#ifdef DVESA
		if(mb->vbe_control_info != 0)
		{		
			vesa_init(mb->vbe_control_info, mb->vbe_mode_info);
			DEBUGP("vesa mode\n");
			
			// Avviamo screenpig
			#ifdef SCREENPIG
				if(mb->vbe_control_info != 0)
				{
					screen_pig_loading(vesa_get_mode_info()->width, vesa_get_mode_info()->height);
					screen_pig_main(vesa_get_mode_info()->width, vesa_get_mode_info()->height);
					goto sd;
				}
			#endif
		}
	#endif
	
	
	// File System Manager
	video_puts_colored(VIDEO_HI_BLUE_BLACK, " -> ");
	video_puts(MSG_FS_INIT);	
	fs_init();

	// Update hardware
	device_update();
	
	#ifdef NETWORK
		// Network Manager
		video_puts_colored(VIDEO_HI_BLUE_BLACK, " -> ");
		video_puts(MSG_NET_INIT);	
		network_init();
	#endif


	// Task test
	/*char *asd[1] = {"system"};
	char *asd2[1] = {"test"};  
	task_exec(task2, asd, 1);
	task_exec(task1, asd2, 1);
	*/
	//term_delete(task_get_from_pid(1)->term);
	
	// Se e' definita la shell, carichiamola
	#ifdef SHELL
		bish();
		goto sd;
	#endif


	// Spegnimento se init viene chiuso
	sd:
		shutdown();
		
		while(1);
}


/**
 * Spegnimento del pc
 */
void shutdown()
{	
	DEBUGP("shutdown()\n");
	
	printf("\n");
	
	// Mandiamo il segnale di kill a tutti i processi
	/*DEBUGP("main.c -> killing all processes\n");*/
	video_puts_colored(VIDEO_HI_BLUE_BLACK, " -> ");
	video_puts(MSG_TASK_KILL);
	task_kill(0);
	
	
	// Umount di tutti i filesystem
	mount_point_t *a;
	mount_point_t *mp = fs_mountpoints;
	while(mp != NULL)
	{
		a = mp->next;
		video_puts_colored(VIDEO_HI_BLUE_BLACK, " -> ");
		printf(MSG_UMOUNTING " %s\n", mp->path);
		fs_umount(mp->path);
		mp = a;
	}
	
	// Fermiamo gli interrupt
	//video_puts("\n");
	asm("cli");
	
	
	video_puts_colored(VIDEO_HI_BLUE_BLACK, "-> ");		
	video_puts(MSG_SHUTDOWN);
	
	// Fermiamo la cpu
	asm("hlt");
	
	while(1);
}
